using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using System.Xml.Serialization;
using BIUtilities.Publish.PpsMonitoringWebService;

namespace BIUtilities.Publish
{
    /// <summary>
    /// Deployment class used to load matching PPS Elements and make transformations. Allows persisting the chagnes to a target PPS Server.
    /// </summary>
    public class PerformancePointDeployUtility
    {
        private string _sourceWorkspace = String.Empty;
        PpsMonitoringWebService.PmService _sourceWebservice = null;
        PpsMonitoringWebService.PmService _targetWebservice = null;
        private string _matchOnProperty = null;
        private string _matchOnValue = null;
        private List<Dashboard> _dashboardList = new List<Dashboard>();
        private List<DataSource> _dataSourceList = new List<DataSource>();
        private List<Indicator> _indicatorList = new List<Indicator>();
        private List<Kpi> _kpiList = new List<Kpi>();
        private List<Scorecard> _scorecardList = new List<Scorecard>();
        private List<ReportView> _reportViewList = new List<ReportView>();
        private object _loadLock = new object();
        private int _loadCount = 0;
         

        /// <summary>
        /// Setup PPS Webservices
        /// </summary>
        public PerformancePointDeployUtility()
        {
        }

        /// <summary>
        /// Set where the PPS reports will be loaded from
        /// </summary>
        public string SourceWebserviceUrl
        {
            get { return _sourceWebservice.Url; }
            set 
            {
                System.Diagnostics.Trace.WriteLine("Pps Source Url: " + value);
                _sourceWebservice = new PpsMonitoringWebService.PmService();
                _sourceWebservice.PreAuthenticate = true;
                _sourceWebservice.UseDefaultCredentials = true;
                _sourceWebservice.Url = value;
            }
        }

        /// <summary>
        /// Set where the PPS reports will be loaded from
        /// </summary>
        public string SourceWorkspace
        {
            get { return _sourceWorkspace; }
            set
            {
                System.Diagnostics.Trace.WriteLine("Pps Source Workspace: " + value);
                _sourceWorkspace = value;
            }
        }

        /// <summary>
        /// Set where the PPS reports will be saved to
        /// </summary>
        public string TargetWebserviceUrl
        {
            get { return _targetWebservice.Url; }
            set 
            {
                System.Diagnostics.Trace.WriteLine("Pps Target Url: " + value);
                _targetWebservice = new PpsMonitoringWebService.PmService();
                _targetWebservice.PreAuthenticate = true;
                _targetWebservice.UseDefaultCredentials = true;
                _targetWebservice.Url = value;
            }
        }

        /// <summary>
        /// When specified, only reports with a given Text PPS Property of the given name will be affected by changes made
        /// </summary>
        public string MatchOnProperty
        {
            get { return _matchOnProperty; }
            set { _matchOnProperty = value; }
        }

        /// <summary>
        /// When specified, only reports with a given value for the matched property will be affected by changes made
        /// </summary>
        public string MatchOnValue
        {
            get { return _matchOnValue; }
            set { _matchOnValue = value; }
        }

        # region "Asynchronously load PPS items and filter for matched elements.
        /// <summary>
        /// Load all of the PPS items from the source server, and filter out only the ones this object is set to match (if matching)
        /// </summary>
        internal void LoadPpsMatchedItems()
        {
            _loadCount = 0;
            if (_sourceWebservice != null)
                LoadPpsMatchItemsFromWebservice();
            else
                LoadPpsMatchItemsFromWorkspace();

        }

        /// <summary>
        /// Serialize the objects from the XML BWSX file and use for publishing and updating
        /// </summary>
        private void LoadPpsMatchItemsFromWorkspace()
        {
            System.Diagnostics.Trace.WriteLine("Starting to Load Elements from Workspace...");

            // Load and serialize the data from the Workspace XML file
            string xmlWorkspace = System.IO.File.ReadAllText(_sourceWorkspace);
            XmlDocument docWorkspace = new XmlDocument();
            docWorkspace.LoadXml(xmlWorkspace);

            XmlAttribute attr = docWorkspace.CreateAttribute("xmlns");
            attr.Value = "http://www.microsoft.com/performancepoint/scorecards";
            docWorkspace.DocumentElement.Attributes.Append(attr);

            // Setup the default namespace for the types
            System.IO.TextReader readerWorkspace = new System.IO.StringReader(docWorkspace.OuterXml);

            // Deserialize into the actual objects
            XmlSerializer deSerializeWorkspace =
                new XmlSerializer(typeof(PpsDataObjects.ScorecardModel));
            PpsDataObjects.ScorecardModel scorecardModel =
                (PpsDataObjects.ScorecardModel)deSerializeWorkspace.Deserialize(readerWorkspace);

            // Filter out the items we want using the standard filtering mechanism
            FilterMatchedDashboards(scorecardModel.DashboardsInWorkspace);
            FilterMatchedScorecards(scorecardModel.ScorecardsInWorkspace);
            FilterMatchedKpis(scorecardModel.KpisInWorkspace);
            FilterMatchedReports(scorecardModel.ReportViewsInWorkspace);
            FilterMatchedDataSources(scorecardModel.DataSourcesInWorkspace);
            FilterMatchedIndicators(scorecardModel.IndicatorsInWorkspace);
        }

        /// <summary>
        /// Use the webservice to get the PPS Items that need to be loaded internally for publishing and updating
        /// </summary>
        private void LoadPpsMatchItemsFromWebservice()
        {
            _sourceWebservice.GetDashboardsCompleted += new GetDashboardsCompletedEventHandler(_sourceWebservice_GetDashboardsCompleted);
            _sourceWebservice.GetScorecardsCompleted += new GetScorecardsCompletedEventHandler(_sourceWebservice_GetScorecardsCompleted);
            _sourceWebservice.GetKpisCompleted += new GetKpisCompletedEventHandler(_sourceWebservice_GetKpisCompleted);
            _sourceWebservice.GetReportViewsCompleted += new GetReportViewsCompletedEventHandler(_sourceWebservice_GetReportViewsCompleted);
            _sourceWebservice.GetDataSourcesCompleted += new GetDataSourcesCompletedEventHandler(_sourceWebservice_GetDataSourcesCompleted);
            _sourceWebservice.GetIndicatorsCompleted += new GetIndicatorsCompletedEventHandler(_sourceWebservice_GetIndicatorsCompleted);

            System.Diagnostics.Trace.WriteLine("Starting to Load Dashboards...");
            _sourceWebservice.GetDashboardsAsync(Guid.NewGuid());
            System.Diagnostics.Trace.WriteLine("Starting to Load Data Sources...");
            _sourceWebservice.GetDataSourcesAsync(Guid.NewGuid());
            System.Diagnostics.Trace.WriteLine("Starting to Load Indicators...");
            _sourceWebservice.GetIndicatorsAsync(Guid.NewGuid());
            System.Diagnostics.Trace.WriteLine("Starting to Load KPIs...");
            _sourceWebservice.GetKpisAsync(Guid.NewGuid());
            System.Diagnostics.Trace.WriteLine("Starting to Load Scorecards...");
            _sourceWebservice.GetScorecardsAsync(Guid.NewGuid());
            System.Diagnostics.Trace.WriteLine("Starting to Load Report Views...");
            _sourceWebservice.GetReportViewsAsync(Guid.NewGuid());

            while (_loadCount < 6)
            {
                System.Threading.Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// Asynchronously load the dashboards
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _sourceWebservice_GetDashboardsCompleted(object sender, GetDashboardsCompletedEventArgs e)
        {
            if (e.Error != null)
                throw new Exception("Error Loading Dashboards", e.Error);
            else if (!e.Cancelled)
            {
                FilterMatchedDashboards(e.Result as Dashboard[]);
                lock (_loadLock)
                {
                    _loadCount++;
                }
                System.Diagnostics.Trace.WriteLine("Finished Loading Dashboards.");
            }
        }

        /// <summary>
        /// Asynchronously load the scorecards
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _sourceWebservice_GetScorecardsCompleted(object sender, GetScorecardsCompletedEventArgs e)
        {
            if (e.Error != null)
                throw new Exception("Error Loading Dashboards", e.Error);
            else if (!e.Cancelled)
            {
                FilterMatchedScorecards(e.Result as Scorecard[]);
                lock (_loadLock)
                {
                    _loadCount++;
                }
                System.Diagnostics.Trace.WriteLine("Finished Loading Scorecards.");
            }
        }

        /// <summary>
        /// Asynchronously load the KPIs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _sourceWebservice_GetKpisCompleted(object sender, GetKpisCompletedEventArgs e)
        {
            if (e.Error != null)
                throw new Exception("Error Loading Dashboards", e.Error);
            else if (!e.Cancelled)
            {
                FilterMatchedKpis(e.Result as Kpi[]);
                lock (_loadLock)
                {
                    _loadCount++;
                }
                System.Diagnostics.Trace.WriteLine("Finished Loading KPIs.");
            }
        }


        /// <summary>
        /// Asynchronously load the report views
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _sourceWebservice_GetReportViewsCompleted(object sender, GetReportViewsCompletedEventArgs e)
        {
            if (e.Error != null)
                throw new Exception("Error Loading Dashboards", e.Error);
            else if (!e.Cancelled)
            {
                FilterMatchedReports(e.Result as ReportView[]);
                lock (_loadLock)
                {
                    _loadCount++;
                }
                System.Diagnostics.Trace.WriteLine("Finished Loading Report Views.");
            }
        }

        /// <summary>
        /// Asynchronously load the data sources
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _sourceWebservice_GetDataSourcesCompleted(object sender, GetDataSourcesCompletedEventArgs e)
        {
            if (e.Error != null)
                throw new Exception("Error Loading Dashboards", e.Error);
            else if (!e.Cancelled)
            {
                FilterMatchedDataSources(e.Result as DataSource[]);
                lock (_loadLock)
                {
                    _loadCount++;
                }
                System.Diagnostics.Trace.WriteLine("Finished Loading Data Sources.");
            }
        }

        /// <summary>
        /// Asynchronously load the indicators
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _sourceWebservice_GetIndicatorsCompleted(object sender, GetIndicatorsCompletedEventArgs e)
        {
            if (e.Error != null)
                throw new Exception("Error Loading Dashboards", e.Error);
            else if (!e.Cancelled)
            {
                FilterMatchedIndicators(e.Result as Indicator[]);
                lock (_loadLock)
                {
                    _loadCount++;
                }
                System.Diagnostics.Trace.WriteLine("Finished Loading Indicators.");
            }
        }

        /// <summary>
        /// Filter out the matching dashboards per the criteria for selection
        /// </summary>
        /// <param name="dashboardList"></param>
        private void FilterMatchedDashboards(Dashboard[] dashboardList)
        {
            foreach (Dashboard item in dashboardList)
            {
                if (IsValidMatch(item.Properties))
                    _dashboardList.Add(item);
            }
        }

        /// <summary>
        /// Filter out the matching scorecards per the criteria for selection
        /// </summary>
        /// <param name="scorecardList"></param>
        private void FilterMatchedScorecards(Scorecard[] scorecardList)
        {
            foreach (Scorecard item in scorecardList)
            {
                if (IsValidMatch(item.Properties))
                    _scorecardList.Add(item);
            }
        }

        /// <summary>
        /// Filter out the matching KPIs per the criteria for selection
        /// </summary>
        /// <param name="kpiList"></param>
        private void FilterMatchedKpis(Kpi[] kpiList)
        {
            foreach (Kpi item in kpiList)
            {
                if (IsValidMatch(item.Properties))
                    _kpiList.Add(item);
            }
        }

        /// <summary>
        /// Filter out the matching reports per the criteria for selection
        /// </summary>
        /// <param name="reportViewList"></param>
        private void FilterMatchedReports(ReportView[] reportViewList)
        {
            foreach (ReportView item in reportViewList)
            {
                if (IsValidMatch(item.Properties))
                    _reportViewList.Add(item);
            }
        }

        /// <summary>
        /// Filter out the matching data sources per the criteria for selection
        /// </summary>
        /// <param name="dataSourceList"></param>
        private void FilterMatchedDataSources(DataSource[] dataSourceList)
        {
            foreach (DataSource item in dataSourceList)
            {
                if (IsValidMatch(item.Properties))
                    _dataSourceList.Add(item);
            }
        }

        /// <summary>
        /// Filter out the matching indicators per the criteria for selection
        /// </summary>
        /// <param name="indicatorList"></param>
        private void FilterMatchedIndicators(Indicator[] indicatorList)
        {
            foreach (Indicator item in indicatorList)
            {
                if (IsValidMatch(item.Properties))
                    _indicatorList.Add(item);
            }
        }


        /// <summary>
        /// Determine if this element is something that we are going to update
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        private bool IsValidMatch(BpmProperty[] properties)
        {
            bool isMatch = true;
            foreach (BpmProperty prop in properties)
            {
                isMatch = true;
                if (_matchOnProperty.Length == 0)
                {
                    // Match all
                }
                else
                {
                    if (prop.DisplayName != _matchOnProperty)
                    {
                        isMatch = false;
                    }
                    else
                    {
                        if (_matchOnValue.Length == 0)
                        {
                            // Match any value
                        } 
                        else
                        {
                            BpmPropertyText textValue = (BpmPropertyText)prop;
                            if (textValue.Text != _matchOnValue)
                                isMatch = false;
                        }
                    }
                }
                if (isMatch)
                    break;
            }
            return isMatch;
        }
        #endregion


        /// <summary>
        /// Elements in PPS embed the name of the element into a property. This method will pull the name out for tracing / diagnostic purposes.
        /// </summary>
        /// <param name="properties">Collection of element BPMProperties</param>
        /// <returns></returns>
        private string GetPrimaryName(BpmProperty[] properties)
        {
            foreach (BpmProperty prop in properties)
            {
                if (prop.DisplayName.ToUpper() == "NAME")
                {
                    BpmPropertyText textValue = (BpmPropertyText)prop;
                    return textValue.Text;
                }
            }
            throw new Exception("Element's Name is not found for element.");
        }

        /// <summary>
        /// Make sure the connections actually work, of not, error
        /// </summary>
        public void VerifySourceConnection()
        {
            if (_sourceWebservice != null)
            {
                _sourceWebservice.GetDataSources();
            }
            else
            {
                if (!System.IO.File.Exists(_sourceWorkspace))
                {
                    throw new Exception(
                        "PPS Source Workspace is not found/accessible: " + _sourceWorkspace);
                }
            }
        }

        /// <summary>
        /// Make sure the connections actually work, of not, error
        /// </summary>
        public void VerifyTargetConnection()
        {
            _targetWebservice.GetDataSources();
        }

        /// <summary>
        /// Add or remove credentials on a PPS Element
        /// </summary>
        /// <param name="credential">NT Credential. ex: redmond\kevinid</param>
        /// <param name="role">PPS Role to assign to the credential: Reader or Editor</param>
        /// <param name="action">Whether to add or remove credential when found</param>
        public void UpdateSecurity(string credential, string role, string action)
        {
            // Update the memberships and assign the new membership array (or the same one if no changes)
            foreach (Dashboard item in _dashboardList)
            {
                item.Memberships = UpdateMembershipSecurity(item.Memberships, credential, role, action);
            }
            foreach (DataSource item in _dataSourceList)
            {
                item.Memberships = UpdateMembershipSecurity(item.Memberships, credential, role, action);
            }

            foreach (Indicator item in _indicatorList)
            {
                item.Memberships = UpdateMembershipSecurity(item.Memberships, credential, role, action);
            }

            foreach (Kpi item in _kpiList)
            {
                item.Memberships = UpdateMembershipSecurity(item.Memberships, credential, role, action);
            }

            foreach (Scorecard item in _scorecardList)
            {
                item.Memberships = UpdateMembershipSecurity(item.Memberships, credential, role, action);
            }

            foreach (ReportView item in _reportViewList)
            {
                item.Memberships = UpdateMembershipSecurity(item.Memberships, credential, role, action);
            }
        }

        /// <summary>
        /// Update the membership collection with the given role, or add if not in the collection
        /// </summary>
        /// <param name="membershipList"></param>
        /// <param name="credential"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        private Membership[] UpdateMembershipSecurity(Membership[] membershipList, string credential, string role, string action)
        {
            bool isFound = false;
            Membership foundMember = null;
            foreach (Membership member in membershipList)
            {
                if (member.Login.ToUpper() == credential.ToUpper())
                {
                    isFound = true;
                    foundMember = member;
                    member.Role = role;
                    break;
                }
            }

            // If the user is not found, we need to add them
            if (action.ToUpper() == "INCLUDE")
            {
                if (!isFound)
                {
                    Trace.WriteLine("Adding credential: " + credential + " for role: " + role);
                    Membership newMember = new Membership();
                    newMember.Role = role;
                    newMember.Login = credential;
                    newMember.Sid = _targetWebservice.GetSidFromLogin(credential);

                    List<Membership> newMemberList = new List<Membership>(membershipList);
                    newMemberList.Add(newMember);
                    return newMemberList.ToArray();
                }
                else
                {
                    return membershipList;
                }
            }
            else // EXCLUDE
            {
                if (isFound)
                {
                    Trace.WriteLine("Removing credential: " + credential);
                    List<Membership> newMemberList = new List<Membership>(membershipList);
                    newMemberList.Remove(foundMember);
                    return newMemberList.ToArray();
                }
                else
                {
                    return membershipList;
                }
            }
            

        }

        /// <summary>
        /// Update the datasources with the new server name
        /// </summary>
        /// <param name="sourceName"></param>
        /// <param name="sourceServer"></param>
        internal void UpdateDataSource(string sourceName, string sourceServer)
        {
            foreach (DataSource ds in _dataSourceList)
            {
                if (GetPrimaryName(ds.Properties).ToUpper() == sourceName.ToUpper())
                {
                    Trace.WriteLine("Updating data source: " + sourceName + " = " + sourceServer );
                    ds.ServerName = sourceServer;
                    break;
                }
            }
        }

        /// <summary>
        /// Update the location of the excel file used for an Excel Services PPS Report
        /// </summary>
        /// <ExcelServicesData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     SharePointSite="http://mybisandbox/" 
        ///     DocumentLibrary="{BD48525B-4383-4EAE-B913-D6E7404D7BF7}" 
        ///     DocumentLibraryRootFolder="/SMEstorage" 
        ///     DocumentLibraryDisplay="SMEstorage" 
        ///     FolderFileName="/SMEstorage/demo report.xlsx" 
        ///     ItemName="" 
        ///     Rows="" 
        ///     Columns="" 
        ///     ViewOptions="AllowInteractivity AllowParameterModification AllowSorting AllowFiltering AllowHyperlinks AllowPivotSpecificOperations AllowPeriodicRefresh" 
        ///     ToolbarType="FullToolbar" 
        ///     ToolbarOptions="AllowInExcelOperations AllowRecalculation AllowNavigation">
        ///   <WorkbookParameters>
        ///     <Keys />
        ///     <Values />
        ///   </WorkbookParameters>
        /// </ExcelServicesData>
        /// <param name="originalSharepointSite"></param>
        /// <param name="newSharepointSite"></param>
        internal void UpdateExcelReportLocation(
            string originalSharepointSite, string newSharepointSite,
            string originalSharepointLibrary, string newSharepointLibrary)
        {
            foreach (ReportView report in _reportViewList)
            {
                if (report.TypeName.ToUpper() == "EXCELSERVICES")
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(report.CustomData);
                    XmlNode node = xmlDoc.SelectSingleNode("/ExcelServicesData");
                    if (node != null)
                    {
                        XmlAttribute sharepointAttribute = node.Attributes["SharePointSite"];
                        bool isFound = ReplaceAttributeFromStart(sharepointAttribute, originalSharepointSite, newSharepointSite, report);

                        if (isFound)
                        {
                            XmlAttribute documentLibraryIdAttribute = node.Attributes["DocumentLibrary"];
                            XmlAttribute documentLibraryRootFolder = node.Attributes["DocumentLibraryRootFolder"];
                            XmlAttribute documentLibraryDisplay = node.Attributes["DocumentLibraryDisplay"];
                            XmlAttribute documentFolderFilename = node.Attributes["FolderFileName"];

                            // Find the new document id
                            SharepointDeployUtility sharepointDeploy = new SharepointDeployUtility();
                            sharepointDeploy.TargetSiteUrl = sharepointAttribute.Value;
                            string currentLibrary = documentLibraryRootFolder.Value.TrimStart('/');
                            string targetLibrary = currentLibrary;

                            // If the library name is changing, we need to update the associated attributes
                            if ((originalSharepointLibrary.Length > 0) && (newSharepointLibrary.Length > 0))
                            {
                                if (currentLibrary.ToUpper() == originalSharepointLibrary.ToUpper())
                                {
                                    targetLibrary = newSharepointLibrary;
                                    documentLibraryRootFolder.Value = 
                                        documentLibraryRootFolder.Value.Replace("/" + currentLibrary, "/" + newSharepointLibrary);
                                    documentLibraryDisplay.Value = 
                                        documentLibraryDisplay.Value.Replace(currentLibrary, newSharepointLibrary);
                                    documentFolderFilename.Value =
                                        documentFolderFilename.Value.Replace("/" + currentLibrary + "/", "/" + newSharepointLibrary + "/");

                                }
                            }
                            Trace.WriteLine("Updating Excel Report Reference to: " + documentFolderFilename.Value);
                            documentLibraryIdAttribute.Value = sharepointDeploy.GetDocumentLibraryId(targetLibrary);

                            report.CustomData = xmlDoc.OuterXml;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Update a parameter which is passed into an Excel Webserviec Report to a static value.
        /// </summary>
        /// <ExcelServicesData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     SharePointSite="http://mybisandbox/" 
        ///     DocumentLibrary="{BD48525B-4383-4EAE-B913-D6E7404D7BF7}" 
        ///     DocumentLibraryRootFolder="/SMEstorage" 
        ///     DocumentLibraryDisplay="SMEstorage" 
        ///     FolderFileName="/SMEstorage/demo report.xlsx" 
        ///     ItemName="" 
        ///     Rows="" 
        ///     Columns="" 
        ///     ViewOptions="AllowInteractivity AllowParameterModification AllowSorting AllowFiltering AllowHyperlinks AllowPivotSpecificOperations AllowPeriodicRefresh" 
        ///     ToolbarType="FullToolbar" 
        ///     ToolbarOptions="AllowInExcelOperations AllowRecalculation AllowNavigation">
        ///   <WorkbookParameters>
        ///     <Keys />
        ///     <Values />
        ///   </WorkbookParameters>
        /// </ExcelServicesData>
        /// <param name="paramName"></param>
        /// <param name="newParamValue"></param>
        internal void UpdateExcelReportParameters(string paramName, string newParamValue)
        {
            foreach (ReportView report in _reportViewList)
            {
                if (report.TypeName.ToUpper() == "EXCELSERVICES")
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(report.CustomData);
                    XmlNode node = xmlDoc.SelectSingleNode("/ExcelServicesData/WorkbookParameters");
                    if (node != null)
                    {
                        int foundNodeIndex = -1;
                        XmlNode keyNode = node.SelectSingleNode("Keys");
                        XmlNode valueNode = node.SelectSingleNode("Values");
                        for (int i = 0; i < keyNode.ChildNodes.Count; i++)
                        {
                            XmlNode childNode = keyNode.ChildNodes[i].ChildNodes[0];
                            if (childNode.InnerText.ToUpper() == paramName.ToUpper())
                            {
                                foundNodeIndex = i;
                                break;
                            }
                        }
                        if (foundNodeIndex > -1)
                        {
                            Trace.WriteLine("Updating Report Param: " + paramName + " = " + newParamValue);
                            XmlNode childNodeValue = valueNode.ChildNodes[foundNodeIndex].ChildNodes[0];
                            childNodeValue.InnerText = newParamValue;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Update the target server used in a PPS Proclarity Analytic Server Report
        /// </summary>
        /// <PasReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     ServerUrl="http://mybisandbox/pas" 
        ///     PageId="dfc586ba-1744-4cc7-a943-c68886cd48a0" 
        ///     PagePath="End to End Demo\Proclarity_End to End Demo\Proclarity Report FY2008" 
        ///     ConfigOptions="dd;" />
        /// <param name="originalPasSite"></param>
        /// <param name="newPasSite"></param>
        internal void UpdatePasReportSites(string originalPasSite, string newPasSite)
        {
            foreach (ReportView report in _reportViewList)
            {
                if (report.TypeName.ToUpper() == "PASPAGE")
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(report.CustomData);
                    XmlNode node = xmlDoc.SelectSingleNode("/PasReportViewData");
                    if (node != null)
                    {
                        XmlAttribute pasSiteAttribute = node.Attributes["ServerUrl"];
                        bool isFound = ReplaceAttributeFromStart(pasSiteAttribute, originalPasSite, newPasSite, report);
                        if (isFound)
                        {
                            XmlAttribute pasPageIdAttribute = node.Attributes["PageId"];
                            XmlAttribute pasPageAttribute = node.Attributes["PagePath"];
                            string pagePath = pasPageAttribute.Value;

                            // Prepare to call pas by getting the library, book and page
                            PasReportDetails pasDetails;
                            string[] pagePathParts = pagePath.Split('\\');
                            if (pagePathParts.Length != 3)
                            {
                                throw new Exception("Page Path does not have 3 segments: " + pagePath);
                            }

                            // Setup the PAS to look up the ID
                            ProclarityAnalyticServerDeployUtility pasDeploy = new ProclarityAnalyticServerDeployUtility();
                            pasDeploy.TargetWebserviceUrl = pasSiteAttribute.Value;
                            pasDeploy.MatchOnLibrary = pagePathParts[0];
                            pasDeploy.MatchOnBook = pagePathParts[1];
                            pasDeploy.MatchOnPage = pagePathParts[2];
                            pasDeploy.LoadPasMatchedItems();

                            if (pasDeploy.FindReport(pagePath, out pasDetails))
                            {
                                Trace.WriteLine("Updating Pas Report Target: " + pagePath);
                                pasPageIdAttribute.Value = pasDetails.BookElement.ID.ToString("D");
                            }
                            else
                            {
                                throw new Exception("PAS Page not on target server: " + pagePath);
                            }

                            report.CustomData = xmlDoc.OuterXml;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Update a web page URL on a web page report
        /// </summary>
        /// <param name="findString"></param>
        /// <param name="replaceString"></param>
        internal void UpdateWebPageUrlReplacements(string findString, string replaceString)
        {
            int counter = 0;
            foreach (ReportView report in _reportViewList)
            {
                if (report.TypeName.ToUpper() == "URL")
                {
                    string urlString = report.CustomData;
                    if (urlString.IndexOf(findString) != -1)
                    {
                        counter++;
                        Trace.WriteLine(
                            string.Format("Replacing Web Page URL '{0}' with '{1}'", findString, replaceString));
                        urlString = urlString.Replace(findString, replaceString);
                        report.CustomData = urlString;
                    }
                }
            }

            if (counter == 0)
            {
                Trace.WriteLine(
                    string.Format("The following string was not found on any matched reports: {0}", findString));
            }
        }

        /// <summary>
        /// Update a site set for an SSRS PPS Report
        /// </summary>
        /// <SqlReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     ReportName="ERwinTableListBySA_v2" 
        ///     ServerAddress="http://mybisandbox/ReportServer " 
        ///     ReportPath="/Biz Metadata/ERwinBrowser/ERwinTableListBySA_v2" 
        ///     ReportMode="ReportCenter" 
        ///     ShowToolbar="false" ShowParameters="false" ShowDocMap="false" Zoom="100" Format="HTML4.0" />
        /// <SqlReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     ReportName="ERwinTableListBySA_v2" 
        ///     ServerAddress="http://mybisandbox/ReportServer " 
        ///     ServerUrl="http://mybisandbox/ReportServer " 
        ///     ReportPath="/Biz Metadata/ERwinBrowser/ERwinTableListBySA_v2" 
        ///     ReportUrl="http://mybisandbox/Reports/Pages/Report.aspx?ItemPath=%2fBiz+Metadata%2fERwinBrowser%2fERwinLibrary" 
        ///     ReportMode="SharepointIntegrated" 
        ///     ShowToolbar="true" ShowParameters="false" ShowDocMap="false" Zoom="100" Format="HTML4.0" />
        /// <param name="originalSite"></param>
        /// <param name="replaceSite"></param>
        internal void UpdateSsrsReportSites(string originalSite, string replaceSite)
        {
            bool isFound = false;
            foreach (ReportView report in _reportViewList)
            {
                if (report.TypeName.ToUpper() == "SQLREPORT")
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(report.CustomData);
                    XmlNode node = xmlDoc.SelectSingleNode("/SqlReportViewData");
                    if (node != null)
                    {
                        XmlAttribute ssrsSiteAttribute = node.Attributes["ServerAddress"];
                        isFound |= ReplaceAttributeFromStart(ssrsSiteAttribute, originalSite, replaceSite, report);

                        // Element does not exist if using ReportCenter mode
                        ssrsSiteAttribute = node.Attributes["ServerUrl"];
                        isFound |= ReplaceAttributeFromStart(ssrsSiteAttribute, originalSite, replaceSite, report);

                        // Element does not exist if using ReportCenter mode
                        ssrsSiteAttribute = node.Attributes["ReportUrl"];
                        isFound |= ReplaceAttributeFromStart(ssrsSiteAttribute, originalSite, replaceSite, report);

                        report.CustomData = xmlDoc.OuterXml;
                    }
                }
            }

            if (isFound)
            {
                Trace.WriteLine(
                    string.Format("The following site was not found in any matched reports: {0}", originalSite));
            }
        }

        /// <summary>
        /// Update a site set for an SSRS PPS Report
        /// </summary>
        /// <SqlReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     ReportName="ERwinTableListBySA_v2" 
        ///     ServerAddress="http://mybisandbox/ReportServer " 
        ///     ReportPath="/Biz Metadata/ERwinBrowser/ERwinTableListBySA_v2" 
        ///     ReportMode="ReportCenter" 
        ///     ShowToolbar="false" ShowParameters="false" ShowDocMap="false" Zoom="100" Format="HTML4.0" />
        /// <SqlReportViewData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        ///     ReportName="ERwinTableListBySA_v2" 
        ///     ServerAddress="http://mybisandbox/ReportServer " 
        ///     ServerUrl="http://mybisandbox/ReportServer " 
        ///     ReportPath="/Biz Metadata/ERwinBrowser/ERwinTableListBySA_v2" 
        ///     ReportUrl="http://mybisandbox/Reports/Pages/Report.aspx?ItemPath=%2fBiz+Metadata%2fERwinBrowser%2fERwinLibrary" 
        ///     ReportMode="SharepointIntegrated" 
        ///     ShowToolbar="true" ShowParameters="false" ShowDocMap="false" Zoom="100" Format="HTML4.0" />
        /// <param name="findPath"></param>
        /// <param name="replacePath"></param>
        internal void UpdateSsrsReportPaths(string findPath, string replacePath)
        {
            bool isFound = false;
            foreach (ReportView report in _reportViewList)
            {
                if (report.TypeName.ToUpper() == "SQLREPORT")
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(report.CustomData);
                    XmlNode node = xmlDoc.SelectSingleNode("/SqlReportViewData");
                    if (node != null)
                    {
                        XmlAttribute ssrsPathAttribute = node.Attributes["ReportPath"];
                        isFound |= ReplaceAttributeFromStart(ssrsPathAttribute, findPath, replacePath, report);

                        report.CustomData = xmlDoc.OuterXml;
                    }
                }
            }

            if (isFound)
            {
                Trace.WriteLine(
                    string.Format("The following path was not found in any matched reports: {0}", findPath));
            }
        }

        /// <summary>
        /// Replace a server name in an attribute
        /// </summary>
        /// <param name="attr"></param>
        /// <param name="findServerName"></param>
        /// <param name="replaceServerName"></param>
        /// <param name="report"></param>
        /// <returns></returns>
        private bool ReplaceAttributeFromStart(XmlAttribute attr, string findString, string replaceString, ReportView report)
        {
            bool isFound = false;
            if (attr != null)
            {
                string stringToSearch = attr.Value;
                if (stringToSearch.ToUpper().StartsWith(findString.ToUpper()))
                {
                    isFound = true;
                    stringToSearch = stringToSearch.Remove(0, findString.Length);
                    stringToSearch = stringToSearch.Insert(0, replaceString);
                    attr.Value = stringToSearch;
                    Trace.WriteLine(
                        string.Format("Replacing {0} {1} with '{2}' for {3}",
                            report.TypeName, attr.Name, stringToSearch, GetPrimaryName(report.Properties)));
                }
            }
            return isFound;
        }


        /// <summary>
        /// Save all matched elements back to PPS with changes made
        /// </summary>
        public void PersistPpsMatchedItems()
        {
            foreach (DataSource item in _dataSourceList)
            {
                Trace.WriteLine("Publishing Datasource: " + this.GetPrimaryName(item.Properties));
                _targetWebservice.CreateDataSource(item);
            }

            foreach (Indicator item in _indicatorList)
            {
                Trace.WriteLine("Publishing Indicator: " + this.GetPrimaryName(item.Properties));
                _targetWebservice.CreateIndicator(item);
            }

            foreach (Kpi item in _kpiList)
            {
                Trace.WriteLine("Publishing KPI: " + this.GetPrimaryName(item.Properties));
                _targetWebservice.CreateKpi(item);
            }

            foreach (ReportView item in _reportViewList)
            {
                Trace.WriteLine("Publishing Report: " + this.GetPrimaryName(item.Properties));
                _targetWebservice.CreateReportView(item);
            }
            foreach (Scorecard item in _scorecardList)
            {
                Trace.WriteLine("Publishing Scorecard: " + this.GetPrimaryName(item.Properties));
                _targetWebservice.CreateScorecard(item);
            }

            foreach (Dashboard item in _dashboardList)
            {
                Trace.WriteLine("Publishing Dashboard: " + this.GetPrimaryName(item.Properties));
                _targetWebservice.CreateDashboard(item);
            }
        }
    }
}
